package com.mju.config;

import java.io.IOException;
import java.time.Duration;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.hash.BeanUtilsHashMapper;
import org.springframework.data.redis.hash.HashMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mju.model.dto.SecurityUser;
import com.mju.model.dto.UserInfo;
import com.mju.utils.JWTUtils;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class JwtAuthFilter extends OncePerRequestFilter {

//	@Autowired
//	private UserDetailsServiceImpl userDetailsServiceImpl;

	@Autowired
	private RedisTemplate redisTemplate;
	@Value("${custom.userExpired}")
	private int userExpired;
	@Value("${custom.userRedisKey}")
	private String userRedisKey;

	@SuppressWarnings("unchecked")
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// 获取token信息
		String header = request.getHeader("Authorization");

		String uri = request.getRequestURI();
		if (uri.equals("/login")) {
			filterChain.doFilter(request, response);
			return;
		}

		// 注意Bearer后面还有一个空格
		if (!StringUtils.hasText(header) || !StringUtils.startsWithIgnoreCase(header, "Bearer ")) {
			// 如果请求头是空的 或者 前置没有以Bearer 开头 那么进入下一个过滤器链
			filterChain.doFilter(request, response);
			return;
		}
		// 把Bearer空格去掉
		String jwt = header.substring(7);
		DecodedJWT decode = JWTUtils.getTokenInfo(jwt);
		if (decode == null) {
			response.setHeader("autologin", "/login");// 自动登录用。调试发现用小写
			filterChain.doFilter(request, response);
			return;
		}
		// 从JWT解析出用户信息JSON格式的字符串
		String userInfoStr = decode.getClaim("userInfo").asString();
		// JSON转换为对象
		ObjectMapper objMapper = new ObjectMapper();
		UserInfo userInfo = objMapper.readValue(userInfoStr, UserInfo.class);

		// 获取认证信息是否存在
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();

		if ((userInfo != null) && (auth == null)) { // 如果账号不为空 并且认证信息是空的
			// 从Redis中获取用户信息，每次都更新用户的过期时间，可以不从数据库获取
			String redisKey = userRedisKey + userInfo.getStudentno();
			Map<String, String> savedUserMap = redisTemplate.opsForHash().entries(redisKey);

			HashMapper<UserInfo, String, String> hashMapper = new BeanUtilsHashMapper<>(UserInfo.class);
			UserInfo savedUserInfo = hashMapper.fromHash(savedUserMap);
			UserDetails savedUser = new SecurityUser(savedUserInfo);
			redisTemplate.expire(redisKey, Duration.ofDays(userExpired));

			// 获取用户信息
			// savedUser=userDetailsServiceImpl.loadUserByUsername(userInfo.getStudentno());
			if (savedUser.getUsername().equals(userInfo.getStudentno())) {

				SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
				// 创建用户认证token 对象
				UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(savedUser, null,
						savedUser.getAuthorities());

				// 把web的请求信息放到Details
				authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
				securityContext.setAuthentication(authToken);// 把用户信息放到 安全上下文中
				SecurityContextHolder.setContext(securityContext);
			}
		}

		filterChain.doFilter(request, response);// 放行
	}
}
